概述
在 Android 中,子线程是不能更新 UI 的,如果在子线程中更新 UI ,程序会抛出异常。这是因为 ViewRootImpl 对 UI 操作进行了验证,这个验证工作是由 ViewROOtImpl 的 checkThred 方法来完成的。
说到消息机制,就离不开 Handler , MessageQueue , Looper 这个三个家伙。对于我们开发者来说接触最多的,应该就是 Handler 了。
Handler :负责发送消息和处理消息。
MessageQueue : 负责存储消息,并提供一个next()方法进行取数据。
Looper : looper 是消息机制中的引擎,负责遍历消息,通过无限循环调用 MessageQueue 中的 next() 获取消息,然后把消息给到 Handler,让 Handler 去处理。
ThreadLocal
ThreadLocal 是一个线程内部的数据存储类,通过它可以再指定的线程中存储数据,数据存储后,只有在指定的线程中可以获取到存储的数据,对于其他线程来说则无法获取到数据。
虽然我们在日常开发中很少使用到,但是在 Android 的系统源码中有不少地方用到了 ThreadLocal ,比如 Looper、ActivityThread 和 AMS 中都用到了。
MessageQueue
MessageQueue 主要包含两个操作:插入和读取,对应的方法为 enqueueMessage 和 next。
enqueueMessage : 是往消息队列中插入一条数据。
next : 是从消息队列中取出一条信息,并将其从消息队列中移除。此方法是一个无限循环的方法,如果消息队列中没有消息,那么 next 方法就会被阻塞在这里。当消息队列中没有消息的时候, Looper 调用 next 也会被阻塞。
尽管 MessageQueue 叫消息队列,但是实际上它是通过一个单链表的数据结构来维护消息列表,单链表在插入和删除上有优势。
Looper
Looper 是消息引擎,负责把消息从 MessageQueue 中取出来,发送给 Handler。
Looper 的构造方法中,会先创造一个MessageQueue,然后将当前线程的对象保存起来。
Handler 工作需要 Looper ,如果没有 Looper 就会报错。如果当前线程没有 Looper 怎么办?很简单,通过 Looper.prepare() 就可以为当前线程创建 Looper,接着通过 Looper.loop() 就可以开启消息循环。除了 prepare 这个方法,还提供了 prepareMainLooper 这个方法用来给主线程也就是 ActivityThread 创建 Looper 使用。
还可以通过 Looper.getMainLooper 获取到主线程的 Looper ,我们可以通过比较当前线程中的 Looper 是不是主线程的 Looper,从而判断当前线程是不是在主线程。
由于 Looper 一旦开启后是不断循环的,应为 Looper.loop() 方法其实是一个死循环,唯一跳出循环的条件是 MessageQueue 的 next 方法返回 null。但是如果 MessageQueue 中没有消息,是会一直阻塞的,并不会返回 null,所以也会导致 Looper 一直阻塞。
Looper 提供了两个方法, quit 和 quitSafely ,两者的区别是,quit 会直接退出,而 quitSafely 是会把当前消息队列中的消息执行完在退出。当调用 Looper 的 quit 方法时,Looper 就会调用 MessageQueue 的 quit 或者 quitSafely 方法来通知消息队列退出,当消息队列被标记位退出状态时,它的 next 方法就会返回 null,Looper 也就会退出循环了。
所以再不需要的时候要退出。
如果 MessageQueue 的 next 返回了新消息, Looper 就会处理这条消息:msg.target.dispatchMessage(msg) ,这里的 msg.target 是指发送这条消息的 Handler 对象,这样 Handler 的发送的消息,最终又交给他的 dispatchMessage 中处理了。
但是要注意,Handler 的 dispatchMessage 的方法是在创建 Handler 时所使用的 Looper 中执行,这样就成功将代码逻辑切换到指定的线程中执行了。
Handler
Handler 主要的工作包含 发送消息 和 处理消息。消息的发送可以通过一系列的 send 或者 post 方法来实现, post 系列的方法最终也是通过 send 来实现的。
Handler 发消息的过程,其实只是向 MessageQueue 中插入一条消息而已,使用的就是我们刚才说的 enqueueMessage 方法。
我们主要还是看一下 dispatchMessage 这个方法。
public void dispatchMessage(Message msg){
if(msg.callbak!=null){ // 这里的callback就是我们通过 post 方法所传递的 Runnable 参数
handleCallback(msg); //这个方法在下面,直接就调用了 Runnable 的run方法
}else{
if(mCallback!=null){
if(mCallback.handleMessage(msg)){
return;
}
}
handleMessage(msg);
}
}
private static void handleCallback(Message massage){
message.callback.run();
}
Handler 处理消息的过程如下:
- 检查 Message 的 Callback 是否为 null ,不为 null 就通过 handleCallback 来处理消息。 Message 的 Callback 是一个 Runnable 对象,实际上就是 Handler 的 post 方法传递过来的的 Runnable 参数。 而 handlerCallback 就是直接调用 Runnable 的 run 方法。
- 检查 mCallback 是否为 null,不为 null 就调用 mCallback 的 handleMessage 方法处理消息,根据 mCallback 的返回值 调用 handleMessage 方法。这个 Callback 是一个接口,定义如下:
public interface Callback{
public boolean handleMessage(Message msg);
}
这个怎么用呢? 我们在创建 Handler 的时候,可以传入一个 Callback ,就是这个东西了。Handler handler = new Handler(callback)
。还要注意,这个接口还有一个返回值,如果返回 true ,后面的 handlerMessage 就不会执行,而是 return 了。
Handler 还支持通过指定的 Looper 进行构造 Handler ,Handler handler =new Handler(looper)
。
我们刚才有说到,可以通过 Looper.getMainLooper 获取到 UI 线程的 Looper, 那我们就可以在子线程创建出 可以向 UI 线程发消息的 Handler 了。